# Create your views here.

import datetime
import os
import json
from PIL import Image, ImageSequence
from drf_yasg2 import openapi
from drf_yasg2.utils import swagger_auto_schema
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.parsers import MultiPartParser

import littleshinserver.settings
from api.models import UploadRecord
from api.utils import failed_response_with_data, success_response_with_data, current_time, remove_last_slash
from api.utils_material import delete_file, urlpath_in_upload_record

from api.mylogger import logger
static_base_dir = littleshinserver.settings.STATIC_BASE_DIR


class AvatarUploadView(viewsets.ViewSet):
    parser_classes = [MultiPartParser]

    @swagger_auto_schema(operation_id='音色头像上传', tags=['upload'],
                         manual_parameters=[
                             openapi.Parameter(name='file', in_=openapi.IN_FORM, type=openapi.TYPE_FILE, required=True, description='上传文件')
                         ],
                         )
    @action(methods=['POST'], detail=False)
    def upload_voicetone(self, request):
        logger.debug(f"avatar_type is voicetone")
        return handle_avatar_uploaded_request(request, "voicetone")


class VtuberUploadView(viewsets.ViewSet):
    parser_classes = [MultiPartParser]

    @swagger_auto_schema(operation_id='主播图片上传', tags=['upload'],
                         manual_parameters=[
                             openapi.Parameter(name='file', in_=openapi.IN_FORM, type=openapi.TYPE_FILE, required=True, description='上传文件')
                         ],
                         )
    @action(methods=['POST'], detail=False)
    def upload_vtuber_pic(self, request):
        logger.debug(f"vtuber_type is pic")
        return handle_vtuber_uploaded_request(request, "pic")

    @swagger_auto_schema(operation_id='主播视频上传', tags=['upload'],
                         manual_parameters=[
                             openapi.Parameter(name='file', in_=openapi.IN_FORM, type=openapi.TYPE_FILE, required=True, description='上传文件')
                         ],
                         )
    @action(methods=['POST'], detail=False)
    def upload_vtuber_video(self, request):
        logger.debug(f"vtuber_type is pic")
        return handle_vtuber_uploaded_request(request, "video")


class MaterialUploadView(viewsets.ViewSet):
    parser_classes = [MultiPartParser]

    @swagger_auto_schema(operation_id='封面上传', tags=['upload'],
                         manual_parameters=[
                             openapi.Parameter(name='kind', in_=openapi.IN_FORM, type=openapi.TYPE_STRING, required=True,
                                               description='上传类型-(本地local/系统sys)'),
                             openapi.Parameter(name='file', in_=openapi.IN_FORM, type=openapi.TYPE_FILE, required=True, description='上传文件-本地')
                         ],
                         )
    @action(methods=['POST'], detail=False)
    def upload_cover(self, request):
        kind = request.data.get('kind', '')
        logger.debug(f"kind is {kind}")
        return handle_material_uploaded_request(request, "cover", kind)

    @swagger_auto_schema(operation_id='图片上传', tags=['upload'],
                         manual_parameters=[
                             openapi.Parameter(name='kind', in_=openapi.IN_FORM, type=openapi.TYPE_STRING, required=True,
                                               description='上传类型-(本地local/系统sys)'),
                             openapi.Parameter(name='file', in_=openapi.IN_FORM, type=openapi.TYPE_FILE, required=True, description='上传文件-本地')
                         ],
                         )
    @action(methods=['POST'], detail=True)
    def upload_pic(self, request):
        kind = request.data.get('kind', '')
        logger.debug(f"kind is {kind}")
        return handle_material_uploaded_request(request, "pic", kind)

    @swagger_auto_schema(operation_id='音频文件上传', tags=['upload'],
                         manual_parameters=[
                             openapi.Parameter(name='kind', in_=openapi.IN_FORM, type=openapi.TYPE_STRING, required=True,
                                               description='上传类型-(本地local/系统sys)'),
                             openapi.Parameter(name='file', in_=openapi.IN_FORM, type=openapi.TYPE_FILE, required=True, description='上传音频文件-本地')
                         ],
                         )
    @action(methods=['POST'], detail=True)
    def upload_audio(self, request):
        kind = request.data.get('kind', '')
        logger.debug(f"kind is {kind}")
        return handle_material_uploaded_request(request, "audio", kind)

    @swagger_auto_schema(operation_id='视频文件上传', tags=['upload'],
                         manual_parameters=[
                             openapi.Parameter(name='kind', in_=openapi.IN_FORM, type=openapi.TYPE_STRING, required=True,
                                               description='上传类型-(本地local/系统sys)'),
                             openapi.Parameter(name='file', in_=openapi.IN_FORM, type=openapi.TYPE_FILE, required=True,
                                               description='上传视频文件-本地')
                         ],
                         )
    @action(methods=['POST'], detail=True)
    def upload_video(self, request):
        kind = request.data.get('kind', '')
        logger.debug(f"kind is {kind}")
        return handle_material_uploaded_request(request, "video", kind)

    @swagger_auto_schema(operation_id='AI视频文件上传', tags=['upload'],
                         manual_parameters=[
                             openapi.Parameter(name='savepath', in_=openapi.IN_FORM, type=openapi.TYPE_STRING, required=True,
                                               description='视频文件在api服务器中的相对路径'),
                             openapi.Parameter(name='mp4filename', in_=openapi.IN_FORM, type=openapi.TYPE_STRING, required=True,
                                               description='视频文件名'),
                             openapi.Parameter(name='file', in_=openapi.IN_FORM, type=openapi.TYPE_FILE, required=True,
                                               description='上传视频文件-本地')
                         ],
                         )
    @action(methods=['POST'], detail=True)
    def upload_ai_video(self, request):
        mp4savepath = request.data.get('savepath', '')
        mp4shortfilename = request.data.get('shortfilename', '')
        mp4urlpath = request.data.get('urlpath', '')
        return handle_ai_video_uploaded_request(request, mp4savepath, mp4shortfilename, mp4urlpath)

    @swagger_auto_schema(operation_id='虚拟人上传', tags=['upload'],
                         manual_parameters=[
                             openapi.Parameter(name='kind', in_=openapi.IN_FORM, type=openapi.TYPE_STRING, required=True,
                                               description='上传类型-(本地local/系统sys)'),
                             openapi.Parameter(name='file', in_=openapi.IN_FORM, type=openapi.TYPE_FILE, required=True,
                                               description='上传虚拟人文件')
                         ],
                         )
    @action(methods=['POST'], detail=True)
    def upload_vhuman(self, request):
        kind = request.data.get('kind', '')
        logger.debug(f"kind is {kind}")
        return handle_material_uploaded_request(request, "vhuman", kind)

    @swagger_auto_schema(operation_id='3d道具上传', tags=['upload'],
                         manual_parameters=[
                             openapi.Parameter(name='kind', in_=openapi.IN_FORM, type=openapi.TYPE_STRING, required=True,
                                               description='上传类型-(本地local/系统sys)'),
                             openapi.Parameter(name='file', in_=openapi.IN_FORM, type=openapi.TYPE_FILE, required=True,
                                               description='上传3d道具文件-本地')
                         ],
                         )
    @action(methods=['POST'], detail=True)
    def upload_model3d(self, request):
        kind = request.data.get('kind', '')
        logger.debug(f"kind is {kind}")
        return handle_material_uploaded_request(request, "model3d", kind)


class DeleteFileViewSet(viewsets.ViewSet):
    @swagger_auto_schema(operation_id='删除上传文件', tags=['upload'], request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        properties={
            "urlpath": openapi.Schema(type=openapi.TYPE_STRING, description="上传路径"),
        }, )
                         )
    @action(methods=['POST'], detail=False)
    def delete_upload_file(self, request):
        data = json.loads(request.body.decode('utf8'))
        urlpath = data.get('urlpath', '')

        delete_ok = delete_file(urlpath)
        if delete_ok:
            UploadRecord.objects.filter(urlpath=urlpath).delete()
            return success_response_with_data(msg="删除文件成功!")
        else:
            return success_response_with_data(msg="删除文件失败!")

    @swagger_auto_schema(operation_id='删除已上传但未使用的文件', tags=['upload'], request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        properties={
        },
    ))
    @action(methods=['POST'], detail=False)
    def delete_unused_upload_file(self, request):
        list_urlpath = urlpath_in_upload_record()
        for urlpath in list_urlpath:
            delete_file(urlpath)
            UploadRecord.objects.filter(urlpath=urlpath).delete()
        return success_response_with_data(msg="删除文件成功!")


def handle_material_uploaded_request(request, material_category, kind):  # material_category可以是 pic audio video cover
    if (kind is None) or (kind == ''):
        return failed_response_with_data(msg="请设置kind参数!")
    files = request.FILES.getlist('file', None)
    if not files:
        return failed_response_with_data(msg="你还没有上传文件!")
    if len(files) > 1:
        return failed_response_with_data(msg="文件传多了吧？")

    # 确定保存的路径，新起一个文件名
    _, this_file_extension = os.path.splitext(files[0].name)
    short_filename_no_ext = current_time() + '_' + material_category
    short_filename = short_filename_no_ext + this_file_extension
    savedir = f'{static_base_dir}/shin/material/{kind}/{material_category}'
    urlpath = f'/static/shin/material/{kind}/{material_category}/{short_filename}'
    # 保存文件
    save_ret = save_uploaded_file(files[0], savedir, short_filename, urlpath)
    res = {
        "kind": kind,
        "urlpath": urlpath,
        "upload_result": save_ret
    }
    if save_ret:
        if material_category == 'pic':
            if this_file_extension.lower() == '.jpg':
                short_filename_cover = short_filename_no_ext + '_cover.jpg'
                savedir_cover = f'{static_base_dir}/shin/material/{kind}/cover'
                urlpath_cover = f'/static/shin/material/{kind}/cover/{short_filename_cover}'
                res["coverpath"] = urlpath_cover
                save_pic_cover(files[0], savedir_cover, short_filename_cover, urlpath_cover)
            if this_file_extension.lower() == '.png':
                short_filename_cover = short_filename_no_ext + '_cover.png'
                savedir_cover = f'{static_base_dir}/shin/material/{kind}/cover'
                urlpath_cover = f'/static/shin/material/{kind}/cover/{short_filename_cover}'
                res["coverpath"] = urlpath_cover
                save_pic_cover(files[0], savedir_cover, short_filename_cover, urlpath_cover)
            if this_file_extension.lower() == '.gif':
                short_filename_cover = short_filename_no_ext + '_cover.jpg'
                savedir_cover = f'{static_base_dir}/shin/material/{kind}/cover'
                urlpath_cover = f'/static/shin/material/{kind}/cover/{short_filename_cover}'
                res["coverpath"] = urlpath_cover
                save_pic_cover(files[0], savedir_cover, short_filename_cover, urlpath_cover)
        elif material_category == 'video':
            short_filename_cover = short_filename_no_ext + '_cover.jpg'
            savedir_cover = f'{static_base_dir}/shin/material/{kind}/cover'
            urlpath_cover = f'/static/shin/material/{kind}/cover/{short_filename_cover}'
            res["coverpath"] = urlpath_cover
            long_filename_cover = os.path.join(savedir_cover, short_filename_cover)
            cmd = f'ffmpeg -y -i {savedir}/{short_filename} -ss 1 -vframes 1 -f image2 {long_filename_cover}'
            print(cmd)
            os.system(cmd)
        return success_response_with_data(res, msg=f"上传{material_category}成功")
    else:
        return failed_response_with_data(res, msg=f"上传{material_category}失败")


def handle_ai_video_uploaded_request(request, mp4savepath, mp4shortfilename, mp4urlpath):
    files = request.FILES.getlist('file', None)
    if not files:
        return failed_response_with_data(msg="你还没有上传文件!")
    if len(files) > 1:
        return failed_response_with_data(msg="文件传多了吧？")
    if mp4savepath == '':
        return failed_response_with_data(msg="未传mp4savepath参数!")
    if mp4shortfilename == '':
        return failed_response_with_data(msg="未传mp4filename参数!")

    # 确定保存的路径，但文件名不是新起的，是传过来的参数
    short_filename_no_ext, this_file_extension = os.path.splitext(mp4shortfilename)
    savedir = f'{static_base_dir}/{mp4savepath}'
    urlpath = mp4urlpath
    if not os.path.isdir(savedir):
        os.makedirs(savedir)
    # 封面路径参数
    short_filename_cover = short_filename_no_ext + '_cover.jpg'
    savedir_cover = f'{static_base_dir}/shin/ai_video/cover'
    urlpath_cover = f'/static/shin/ai_video/cover/{short_filename_cover}'
    if not os.path.isdir(savedir_cover):
        os.makedirs(savedir_cover)
    # 保存文件
    save_ret = save_uploaded_file(files[0], savedir, mp4shortfilename, urlpath)
    res = {
        "kind": 'local',
        "urlpath": urlpath,
        "coverpath": urlpath_cover,
        "upload_result": save_ret
    }
    if save_ret:
        # 调用ffmpeg创建封面
        res["coverpath"] = urlpath_cover
        long_filename_cover = os.path.join(savedir_cover, short_filename_cover)
        cmd = f'ffmpeg -y -i {savedir}/{mp4shortfilename} -ss 1 -vframes 1 -f image2 {long_filename_cover}'
        print(cmd)
        os.system(cmd)
        return success_response_with_data(res, msg=f"上传AI视频成功")
    else:
        return failed_response_with_data(res, msg=f"上传AI视频失败")


def handle_avatar_uploaded_request(request, avatar_type):  # avatar_type可以是 voicetone
    files = request.FILES.getlist('file', None)
    if not files:
        return failed_response_with_data(msg="你还没有上传文件!")
    if len(files) > 1:
        return failed_response_with_data(msg="文件传多了吧？")

    _, this_file_extension = os.path.splitext(files[0].name)
    short_filename_no_ext = current_time() + '_' + avatar_type
    short_filename = short_filename_no_ext + this_file_extension
    savedir = f'{static_base_dir}/shin/avatar/{avatar_type}'
    urlpath = f'/static/shin/avatar/{avatar_type}/{short_filename}'
    # 保存文件
    save_ret = save_uploaded_file(files[0], savedir, short_filename, urlpath)
    res = {
        "avatar_type": avatar_type,
        "urlpath": urlpath,
        "upload_result": save_ret
    }
    if save_ret:
        return success_response_with_data(res, msg=f"上传{avatar_type}成功")
    else:
        return failed_response_with_data(res, msg=f"上传{avatar_type}失败")


def handle_vtuber_uploaded_request(request, vtuber_type):  # vtuber_type: pic, video
    files = request.FILES.getlist('file', None)
    if not files:
        return failed_response_with_data(msg="你还没有上传文件!")
    if len(files) > 1:
        return failed_response_with_data(msg="文件传多了吧？")

    _, this_file_extension = os.path.splitext(files[0].name)
    short_filename_no_ext = current_time() + '_' + vtuber_type
    short_filename = short_filename_no_ext + this_file_extension
    savedir = f'{static_base_dir}/shin/vtuber/{vtuber_type}'
    urlpath = f'/static/shin/vtuber/{vtuber_type}/{short_filename}'

    save_ret = save_uploaded_file(files[0], savedir, short_filename, urlpath)
    res = {
        "vtuber_type": vtuber_type,
        "urlpath": urlpath,
        "upload_result": save_ret
    }
    if save_ret:
        return success_response_with_data(res, msg=f"上传{vtuber_type}成功")
    else:
        return failed_response_with_data(res, msg=f"上传{vtuber_type}失败")


def save_uploaded_file(file, save_dir, short_filename, urlpath):
    s_save_dir = remove_last_slash(save_dir)
    if not os.path.isdir(s_save_dir):
        os.makedirs(s_save_dir)

    # 上传文件记录为未使用，添加material的时候更新为已使用
    try:
        UploadRecord.objects.create(urlpath=urlpath, status=False)
    except:
        logger.debug('urlpath存UploadRecord失败')
        pass

    # 保存文件
    long_filename = os.path.join(s_save_dir + '/' + short_filename)
    try:
        with open(long_filename, 'wb+') as destination:
            for chunk in file.chunks():
                destination.write(chunk)
            destination.close()
        return True
    except:
        return False


# 图片类型的资源，保存其cover
def save_pic_cover(file, save_dir, short_filename, urlpath_cover):
    s_save_dir = remove_last_slash(save_dir)
    if not os.path.isdir(s_save_dir):
        os.makedirs(s_save_dir)

    _, this_pic_extension = os.path.splitext(file.name)
    long_filename_cover = os.path.join(s_save_dir + '/' + short_filename)

    im = Image.open(file)
    if im.format == 'JPEG' or im.format == 'JPG':
        im.thumbnail((160, 260))
        im.save(long_filename_cover, "JPEG")
    elif im.format == 'PNG':
        im.thumbnail((160, 260))
        im.save(long_filename_cover, "PNG")
    elif im.format == 'GIF':
        frames = ImageSequence.all_frames(im)
        len_frames = len(frames)
        im = frames[len_frames - 1].copy()
        im = im.convert('RGB')
        im.thumbnail((160, 260))
        im.save(long_filename_cover, "JPEG")
    im.close()
    try:
        UploadRecord.objects.create(urlpath=urlpath_cover, status=False)
    except:
        logger.debug('save_pic_cover,存UploadRecord失败,urlpath_cover is ' + urlpath_cover)
        pass

    return
